﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace SpriteMaker
{
    public class Bound
    {
        public enum BoundType
        {
            Unknown,
            Rectangle,
            ConvexPolygon,
            Circle,
        }
        virtual public BoundType GetType()
        {
            return BoundType.Unknown;
        }
        virtual public String GetString()
        {
            return null;
        }
    }
    public class BoundRectangle: Bound
    {
        override public BoundType GetType()
        {
            return BoundType.Rectangle;
        }
        override public String GetString()
        {
            return "Rectangle: " + x + " " + y + " " + w + " " + h;
        }
        public int x, y, w = 20, h = 20;
    }
    public class BoundCircle : Bound
    {
        override public BoundType GetType()
        {
            return BoundType.Circle;
        }
        override public String GetString()
        {
            return "Circle: " + x + " " + y + " " + r;
        }
        public int x, y, r = 10;
    }
    public class BoundConvexPolygon : Bound
    {
        override public BoundType GetType()
        {
            return BoundType.ConvexPolygon;
        }
    }
    public class BoundGroup
    {
        public List<Bound> bounds = new List<Bound>();
        public int id;
        public String description = "";
    }
    public class Module
    {
        public int id, x, y, width, height;
        public String description = "";
    }
    public class FrameModule
    {
        public Module module;
        public int x, y;
    }
    public class Frame
    {
        public int id;
        public String description = "";
        public List<FrameModule> frameModuleList = new List<FrameModule>();
        public List<BoundGroup> boundGroups = new List<BoundGroup>();
        public FrameModule GetFrameModuleAt(int index)
        {
            return frameModuleList[index];
        }
        public void SetFrameModuleValueAt(int index, Module module, int x, int y)
        {
            frameModuleList[index].module = module;
            frameModuleList[index].x = x;
            frameModuleList[index].y = y;
        }
        public int AddBoundingGroup(String desc)
        {
            for (int i = 0; i <= boundGroups.Count; i++)
            {
                bool found = false;
                for (int j = 0; j < boundGroups.Count; j++)
                {
                    if (i == boundGroups[j].id)
                    {
                        found = true;
                        break;
                    }
                }
                if (found == false)
                {
                    BoundGroup bg = new BoundGroup();
                    bg.id = i;
                    bg.description = desc;
                    boundGroups.Add(bg);
                    return i;
                }
            }
            BoundGroup bg2 = new BoundGroup();
            bg2.id = 0;
            bg2.description = desc;
            boundGroups.Add(bg2);
            return 0;
        }
        public BoundGroup GetBoundGroup(int id)
        {
            for (int i = 0; i < boundGroups.Count; i++)
            {
                if (boundGroups[i].id == id)
                {
                    return boundGroups[i];
                }
            }
            return null;
        }
    }
    public class AnimFrame
    {
        public Frame frame;
        public int time = 100;
    }
    public class Anim
    {
        public int id;
        public String description = "";
        public List<AnimFrame> animFrameList = new List<AnimFrame>();
        public bool loop;
        public void SetAnimFrameValueAt(int index, Frame frame, int time)
        {
            animFrameList[index].frame = frame;
            animFrameList[index].time = time;
        }
    }
    public class Sprite
    {
        public static Sprite spriteInstance;
        public List<Module> moduleList = new List<Module>();
        public List<Frame> frameList = new List<Frame>();
        public List<Anim> animList = new List<Anim>();
        public String imageName = "";
        public String spriteName = "New Sprite";
        public formMain formInstance;

        public int AddModule()
        {
            for (int i = 0; i <= moduleList.Count; i++)
            {
                bool found = false;
                for (int j = 0; j < moduleList.Count; j++)
                {
                    if (i == moduleList[j].id)
                    {
                        found = true;
                        break;
                    }
                }
                if (found == false)
                {
                    Module module = new Module();
                    module.id = i;
                    module.width = 20;
                    module.height = 20;
                    moduleList.Add(module);
                    return i;
                }
            }
            return 0;
        }
        public void AddModule(int id)
        {
            for (int i = 0; i < moduleList.Count; i++)
            {
                if (moduleList[i].id == id)
                {
                    throw new Exception("ID duplicated");
                }
            }
            Module module = new Module();
            module.id = id;
            module.width = 20;
            module.height = 20;
            moduleList.Add(module);
        }
        public int AddFrame()
        {
            for (int i = 0; i <= frameList.Count; i++)
            {
                bool found = false;
                for (int j = 0; j < frameList.Count; j++)
                {
                    if (i == frameList[j].id)
                    {
                        found = true;
                        break;
                    }
                }
                if (found == false)
                {
                    Frame frame = new Frame();
                    frame.id = i;
                    frameList.Add(frame);
                    return i;
                }
            }
            return 0;
        }
        public void AddFrame(int id)
        {
            for (int i = 0; i < frameList.Count; i++)
            {
                if (frameList[i].id == id)
                {
                    throw new Exception("ID duplicated");
                }
            }
            Frame frame = new Frame();
            frame.id = id;
            frameList.Add(frame);
        }
        public int AddFrameModule(int frameID)
        {
            if (moduleList.Count == 0)
            {
                throw new Exception("Please add module first");
            }
            else
            {
                FrameModule frameModule = new FrameModule();
                frameModule.module = moduleList[0];
                frameModule.x = 0;
                frameModule.y = 0;
                Frame frame = GetFrame(frameID);
                frame.frameModuleList.Add(frameModule);
                return frameModule.module.id;
            }
        }
        public int AddAnim()
        {
            for (int i = 0; i <= animList.Count; i++)
            {
                bool found = false;
                for (int j = 0; j < animList.Count; j++)
                {
                    if (i == animList[j].id)
                    {
                        found = true;
                        break;
                    }
                }
                if (found == false)
                {
                    Anim anim = new Anim();
                    anim.id = i;
                    animList.Add(anim);
                    return i;
                }
            }
            return 0;
        }
        public int AddAnimFrame(int animID)
        {
            if (frameList.Count == 0)
            {
                throw new Exception("Please add frame first");
            }
            else
            {
                AnimFrame animFrame = new AnimFrame();
                animFrame.frame = frameList[0];
                Anim anim = GetAnim(animID);
                anim.animFrameList.Add(animFrame);
                return animFrame.frame.id;
            }
        }
        public void RemoveModule(int id)
        {
            for (int i = 0; i < moduleList.Count; i++)
            {
                if (moduleList[i].id == id)
                {
                    moduleList.RemoveAt(i);
                    return;
                }
            }
        }
        public void RemoveModuleAt(int index)
        {
            moduleList.RemoveAt(index);
        }
        public void SetModuleValueAt(int index, int id, int x, int y, int w, int h, String desc)
        {
            for (int i = 0; i < moduleList.Count; i++)
            {
                if (i != index && moduleList[i].id == id)
                {
                    throw(new Exception("id duplicated"));
                }
            }
            moduleList[index].id = id;
            moduleList[index].x = x;
            moduleList[index].y = y;
            moduleList[index].width = w;
            moduleList[index].height = h;
            moduleList[index].description = desc;
        }
        public void SetModuleValue(int id, int newId, int x, int y, int w, int h, String desc)
        {
            if (id != newId)
            {
                for (int i = 0; i < moduleList.Count; i++)
                {
                    if (moduleList[i].id == newId)
                    {
                        throw (new Exception("id duplicated"));
                    }
                }
            }
            for (int i = 0; i < moduleList.Count; i++)
            {
                if (moduleList[i].id == id)
                {
                    moduleList[i].id = newId;
                    moduleList[i].x = x;
                    moduleList[i].y = y;
                    moduleList[i].width = w;
                    moduleList[i].height = h;
                    moduleList[i].description = desc;
                    break;
                }
            }
        }
        public void SetFrameValueAt(int index, int id, String desc)
        {
            for (int i = 0; i < frameList.Count; i++)
            {
                if (i != index && frameList[i].id == id)
                {
                    throw (new Exception("id duplicated"));
                }
            }
            frameList[index].id = id;
            frameList[index].description = desc;
        }
        public void SetFrameValue(int id, int newId, String desc)
        {
            if (id != newId)
            {
                for (int i = 0; i < frameList.Count; i++)
                {
                    if (frameList[i].id == newId)
                    {
                        throw (new Exception("id duplicated"));
                    }
                }
            }
            for (int i = 0; i < frameList.Count; i++)
            {
                if (frameList[i].id == id)
                {
                    frameList[i].id = newId;
                    frameList[i].description = desc;
                    break;
                }
            }
        }
        public void SetAnimValue(int id, int newId, bool loop, String desc)
        {
            if (id != newId)
            {
                for (int i = 0; i < animList.Count; i++)
                {
                    if (animList[i].id == newId)
                    {
                        throw (new Exception("id duplicated"));
                    }
                }
            }
            for (int i = 0; i < animList.Count; i++)
            {
                if (animList[i].id == id)
                {
                    animList[i].id = newId;
                    animList[i].loop = loop;
                    animList[i].description = desc;
                    break;
                }
            }
        }
        public Module GetModule(int id)
        {
            for (int i = 0; i < moduleList.Count; i++)
            {
                if (moduleList[i].id == id)
                {
                    return moduleList[i];
                }
            }
            return null;
        }
        public Frame GetFrame(int id)
        {
            for (int i = 0; i < frameList.Count; i++)
            {
                if (frameList[i].id == id)
                {
                    return frameList[i];
                }
            }
            return null;
        }
        public Anim GetAnim(int id)
        {
            for (int i = 0; i < animList.Count; i++)
            {
                if (frameList[i].id == id)
                {
                    return animList[i];
                }
            }
            return null;
        }
        public char[] ToCharArray()
        {
            String temp = "";
            temp += "-version\r\n";
            temp += "001\r\n";
            temp += "-image\r\n";
            temp += imageName + "\r\n";
            temp += "999,999\r\n";
            temp += "-module\r\n";
            temp += moduleList.Count().ToString() + "\r\n";
            for (int i = 0; i < moduleList.Count(); i++)
            {
                temp += moduleList[i].id.ToString() + ",";
                temp += moduleList[i].x.ToString() + ",";
                temp += moduleList[i].y.ToString() + ",";
                temp += moduleList[i].width.ToString() + ",";
                temp += moduleList[i].height.ToString() + ",";
                temp += moduleList[i].description + "\r\n";
            }
            temp += "-frame\r\n";
            temp += frameList.Count().ToString() + "\r\n";
            for (int i = 0; i < frameList.Count(); i++)
            {
                temp += frameList[i].id.ToString() + ",";
                temp += frameList[i].frameModuleList.Count().ToString() + "\r\n";
                for (int j = 0; j < frameList[i].frameModuleList.Count(); j++)
                {
                    temp += "-frameModule\r\n";
                    temp += frameList[i].frameModuleList[j].module.id + ",";
                    temp += frameList[i].frameModuleList[j].x + ",";
                    temp += frameList[i].frameModuleList[j].y + "\r\n";
                }
            }
            temp += "-anim\r\n";
            temp += animList.Count().ToString() + "\r\n";
            for (int i = 0; i < animList.Count(); i++)
            {
                temp += animList[i].id.ToString() + ",";
                temp += animList[i].loop.ToString() + ",";
                temp += animList[i].animFrameList.Count().ToString() + "\r\n";
                for (int j = 0; j < animList[i].animFrameList.Count(); j++)
                {
                    temp += "-frameModule\r\n";
                    temp += animList[i].animFrameList[j].frame.id + ",";
                    temp += animList[i].animFrameList[j].time + "\r\n";
                }
            }
            return temp.ToCharArray();
        }
        public String ToStringXML()
        {
            XDocument doc = new XDocument();
            XElement root = new XElement("sprite", new XAttribute("name", spriteName), new XAttribute("version", "0.0.1"));
            root.Add(new XElement("image", new XAttribute("name", imageName)));
            XElement moduleListXML = new XElement("moduleList");
            float imWidth = formInstance.GetImage().Width;
            float imHeight = formInstance.GetImage().Height;
            foreach(Module module in moduleList)
            {
                moduleListXML.Add(new XElement("module", new XAttribute("id", module.id)
                                                        , new XAttribute("x", module.x)
                                                        , new XAttribute("y", module.y)
                                                        , new XAttribute("w", module.width)
                                                        , new XAttribute("h", module.height)
                                                        , new XAttribute("left_OpenGL", ((float)module.x) / imWidth)
                                                        , new XAttribute("right_OpenGL", ((float)module.x + module.width) / imWidth)
                                                        , new XAttribute("top_OpenGL", ((float)module.y) / imHeight)
                                                        , new XAttribute("bottom_OpenGL", ((float)module.y + module.height) / imHeight)
                                                        , new XAttribute("desc", module.description)));
            }
            root.Add(moduleListXML);
            XElement frameListXML = new XElement("frameList");
            foreach (Frame frame in frameList)
            {
                XElement frameXML = new XElement("frame", new XAttribute("id", frame.id), new XAttribute("desc", frame.description));
                foreach (FrameModule frameModule in frame.frameModuleList)
                {
                    frameXML.Add(new XElement("frameModule"
                        , new XAttribute("id",frameModule.module.id)
                        , new XAttribute("x", frameModule.x)
                        , new XAttribute("y", frameModule.y)));
                }
                foreach (BoundGroup bg in frame.boundGroups)
                {
                    XElement boundGroupXML = new XElement("boundGroup", new XAttribute("id", bg.id), new XAttribute("desc", bg.description));
                    foreach (Bound bound in bg.bounds)
                    {
                        XElement boundXML = new XElement("bound", new XAttribute("type", bound.GetType().ToString()));
                        switch (bound.GetType())
                        {
                            case Bound.BoundType.Circle:
                                boundXML.Add(new XAttribute("x", ((BoundCircle)bound).x)
                                    , new XAttribute("y", ((BoundCircle)bound).y)
                                    , new XAttribute("r", ((BoundCircle)bound).r));
                                break;
                            case Bound.BoundType.Rectangle:
                                boundXML.Add(new XAttribute("x", ((BoundRectangle)bound).x)
                                    , new XAttribute("y", ((BoundRectangle)bound).y)
                                    , new XAttribute("w", ((BoundRectangle)bound).w)
                                    , new XAttribute("h", ((BoundRectangle)bound).h));
                                break;
                            case Bound.BoundType.ConvexPolygon:
                                break;
                            case Bound.BoundType.Unknown:
                                break;
                        }
                        boundGroupXML.Add(boundXML);
                    }
                    frameXML.Add(boundGroupXML);
                }
                frameListXML.Add(frameXML);
            }
            root.Add(frameListXML);
            XElement animListXML = new XElement("animList");
            foreach (Anim anim in animList)
            {
                XElement animXML = new XElement("anim", new XAttribute("id", anim.id), new XAttribute("loop", anim.loop), new XAttribute("desc", anim.description));
                foreach (AnimFrame animFrame in anim.animFrameList)
                {
                    animXML.Add(new XElement("animFrame", new XAttribute("id", animFrame.frame.id), new XAttribute("time", animFrame.time)));
                }
                animListXML.Add(animXML);
            }
            root.Add(animListXML);
            doc.Add(root);
            return doc.ToString() + "\0";
        }
        public void FromStringXML(String data)
        {
            this.animList.Clear();
            this.frameList.Clear();
            this.moduleList.Clear();
            XDocument document = XDocument.Parse(data.Trim('\0'), LoadOptions.None);
            XElement root = document.Element("sprite");
            XElement image = root.Element("image");
            this.imageName = image.Attribute("name").Value;
            XElement moduleListXML = root.Element("moduleList");
            foreach (XElement moduleXML in moduleListXML.Elements("module"))
            {
                Module module = new Module();
                module.id = Convert.ToInt32(moduleXML.Attribute("id").Value);
                module.x = Convert.ToInt32(moduleXML.Attribute("x").Value);
                module.y = Convert.ToInt32(moduleXML.Attribute("y").Value);
                module.width = Convert.ToInt32(moduleXML.Attribute("w").Value);
                module.height = Convert.ToInt32(moduleXML.Attribute("h").Value);
                module.description = moduleXML.Attribute("desc").Value;
                moduleList.Add(module);
            }
            XElement frameListXML = root.Element("frameList");
            foreach (XElement frameXML in frameListXML.Elements("frame"))
            {
                Frame frame = new Frame();
                frame.id = Convert.ToInt32(frameXML.Attribute("id").Value);
                frame.description = frameXML.Attribute("desc").Value;
                foreach (XElement frameModuleXML in frameXML.Elements("frameModule"))
                {
                    FrameModule frameModule = new FrameModule();
                    int id = Convert.ToInt32(frameModuleXML.Attribute("id").Value);
                    foreach (Module module in moduleList)
                    {
                        if (module.id == id)
                        {
                            frameModule.module = module;
                            break;
                        }
                    }
                    frameModule.x = Convert.ToInt32(frameModuleXML.Attribute("x").Value);
                    frameModule.y = Convert.ToInt32(frameModuleXML.Attribute("y").Value);
                    frame.frameModuleList.Add(frameModule);
                }
                foreach (XElement boundGroupXML in frameXML.Elements("boundGroup"))
                {
                    BoundGroup boundGroup = new BoundGroup();
                    boundGroup.id = Convert.ToInt32(boundGroupXML.Attribute("id").Value);
                    boundGroup.description = boundGroupXML.Attribute("desc").Value;
                    foreach (XElement boundXML in boundGroupXML.Elements("bound"))
                    {
                        String type = boundXML.Attribute("type").Value;
                        switch (type)
                        {
                            case "Circle":
                                {
                                    BoundCircle bound = new BoundCircle();
                                    bound.x = Convert.ToInt32(boundXML.Attribute("x").Value);
                                    bound.y = Convert.ToInt32(boundXML.Attribute("y").Value);
                                    bound.r = Convert.ToInt32(boundXML.Attribute("r").Value);
                                    boundGroup.bounds.Add(bound);
                                }
                                break;
                            case "Rectangle":
                                {
                                    BoundRectangle bound = new BoundRectangle();
                                    bound.x = Convert.ToInt32(boundXML.Attribute("x").Value);
                                    bound.y = Convert.ToInt32(boundXML.Attribute("y").Value);
                                    bound.w = Convert.ToInt32(boundXML.Attribute("w").Value);
                                    bound.h = Convert.ToInt32(boundXML.Attribute("h").Value);
                                    boundGroup.bounds.Add(bound);
                                }
                                break;
                        }
                    }
                    frame.boundGroups.Add(boundGroup);
                }
                frameList.Add(frame);
            }
            XElement animListXML = root.Element("animList");
            foreach (XElement animXML in animListXML.Elements("anim"))
            {
                Anim anim = new Anim();
                anim.id = Convert.ToInt32(animXML.Attribute("id").Value);
                anim.loop = Convert.ToBoolean(animXML.Attribute("loop").Value);
                anim.description = animXML.Attribute("desc").Value;
                foreach (XElement animFrameXML in animXML.Elements("animFrame"))
                {
                    AnimFrame animFrame = new AnimFrame();
                    int id = Convert.ToInt32(animFrameXML.Attribute("id").Value);
                    foreach (Frame frame in frameList)
                    {
                        if (frame.id == id)
                        {
                            animFrame.frame = frame;
                            break;
                        }
                    }
                    animFrame.time = Convert.ToInt32(animFrameXML.Attribute("time").Value);
                    anim.animFrameList.Add(animFrame);
                }
                animList.Add(anim);
            }
        }
        public void FromString(String data)
        {
            animList.Clear();
            frameList.Clear();
            moduleList.Clear();
            String temp2 = data.Replace("\r\n", ";");
            String[] temp = temp2.Split(new char[]{';'});

        }
        public void SetFrameMoudleValue(int frameId, int frameModuleIndex, int newId, int x, int y)
        {
            Module module = null;
            for(int i = 0;i < moduleList.Count;i++)
            {
                if(moduleList[i].id == newId)
                {
                    module = moduleList[i];
                    break;
                }
            }
            if(module == null)
            {
                throw new Exception("Moudle not found");
            }
            for (int i = 0; i < frameList.Count; i++)
            {
                if (frameList[i].id == frameId)
                {
                    frameList[i].SetFrameModuleValueAt(frameModuleIndex, module, x, y);
                    break;
                }
            }
        }
        public void SetAnimFrameValue(int animId, int animFrameIndex, int newId, int time)
        {
            Frame frame = null;
            for (int i = 0; i < frameList.Count; i++)
            {
                if (frameList[i].id == newId)
                {
                    frame = frameList[i];
                    break;
                }
            }
            if (frame == null)
            {
                throw new Exception("Frame not found");
            }
            for (int i = 0; i < animList.Count; i++)
            {
                if (animList[i].id == animId)
                {
                    animList[i].SetAnimFrameValueAt(animFrameIndex, frame, time);
                    break;
                }
            }
        }
    }
}
